package com.yeban.bams.service;

import com.yeban.bams.dao.IDao;
import com.yeban.bams.dao.JDBCDao;
import com.yeban.bams.entity.*;
import com.yeban.bams.exception.*;
import com.yeban.bams.vo.RankVo;
import com.yeban.bams.server.log.LogAction;

import java.util.*;

/**
 * @Desc TODO
 * @Author yeban
 * @Date 2025/10/10
 */
public class Bank {

    // 私有的 静态的 本类实例
    private static Bank instance;
    // 面向接口编程 可拔插式编程
    // private IDao<Account,Account[]> dao;
    // private IDao<Account,ArrayList<Account>> dao;
    private IDao<Account, List<Account>> dao;

    // 构造方法（供CGLIB子类可访问）
    protected Bank() {
        // dao = new ArrayDao();
        // dao = new ArrayListDao();
        // dao = new FileDao();
        dao = new JDBCDao();
    }

    // 公开的 静态的 获取本类实例的方法
    public static Bank getInstance() {
        if (instance == null) {
            instance = new Bank();
        }
        return instance;
    }

    /**
     * 用户开户
     * 
     * @param password   账户密码
     * @param repassword 重复密码
     * @param name       真实姓名
     * @param personID   身份证号
     * @param email      邮箱
     * @param type       选择账户类型 项目需求规定账户类型：0 – 储蓄账户 1 – 信用账户 2 – 可贷款储蓄账户 3– 可贷款信用账户
     * @return Account 银行账户
     */
    @LogAction(value = "注册", whoArgIndex = 2)
    public Account register(String password, String repassword, String name, String personID, String email, int type)
            throws RegisterException, TypeException {

        Account acc = null;
        // 判断输入是否异常
        if (password == null || password.equals("") || repassword == null || repassword.equals("") ||
                name == null || name.equals("") || personID == null || personID.equals("")) {
            throw new RegisterException("账户信息不能为空！");
        }
        if (!password.equals(repassword)) {
            throw new RegisterException("两次输入的密码不一致！");
        }

        // 生成ID
        Long id = dao.createID();
        if (id <= 0) {
            throw new RegisterException("ID生成有误！");
        }

        // 0 – 储蓄账户 1 – 信用账户 2 – 可贷款储蓄账户 3– 可贷款信用账户
        switch (type) {
            case 0: // 储蓄账户
                acc = new SavingAccount(id, password, name, personID, email, 0.0);
                break;
            case 1: // 信用账户
                acc = new CreditAccount(id, password, name, personID, email, 0.0, 1500.0);
                break;
            case 2: // 可贷款储蓄账户
                acc = new LoanableSavingsAccount(id, password, name, personID, email, 0.0, 0L);
                break;
            case 3: // 可贷款信用账户
                acc = new LoanableCreditAccount(id, password, name, personID, email, 0.0, 1500.0, 0L);
                break;
            default:
                throw new TypeException("该账户类型不合法！开户失败！");
        }
        // 注册账户信息
        if (acc != null) {
            boolean falg = dao.insert(acc); // 把这个新账户存到Dao层中
            if (falg) {
                System.out.println("开户成功！");
            } else {
                throw new RegisterException("开户失败");
            }
        }
        return acc;
    }

    /**
     * 用户登录(login)
     * 
     * @param id       用户ID
     * @param password 账户密码
     * @return Account 银行账户
     */
    @LogAction(value = "登录", whoArgIndex = 0)
    public Account login(Long id, String password) throws AccountNullException, NotFoundException {
        if (id == null || password == null || id.equals("") || password.equals("")) {
            throw new AccountNullException("账户信息不能为空！");
        }
        // 查询
        Account acc = dao.selectOne(id, password);
        if (acc != null) {
            System.out.println("登录成功！欢迎您！");
        } else {
            throw new NotFoundException("账户与密码不匹配！登录失败！");
        }
        return acc;
    }

    /**
     * 用户存款
     * 
     * @param id    用户ID
     * @param money 存款金额
     * @return Account 银行账户
     */
    @LogAction(value = "存款", whoArgIndex = 0)
    public Account deposit(Long id, double money) throws AccountNullException, NotFoundException {
        if (id == null) {
            throw new AccountNullException("账户ID不能为空！");
        }
        // 查询
        Account acc = dao.selectOne(id);
        if (acc == null) {
            throw new NotFoundException("该账户不存在！");
        }
        acc.deposit(money);
        dao.update(acc);
        return dao.selectOne(id); // 确保返回的对象与文件中一致
    }

    /**
     * 用户取款
     * 
     * @param id       用户ID
     * @param password 账户密码
     * @param money    取款金额
     * @return Account 银行账户
     */
    @LogAction(value = "取款", whoArgIndex = 0)
    public Account withdraw(Long id, String password, double money)
            throws AccountNullException, NotFoundException, BalanceNotEnoughException {
        if (id == null || password == null || password.equals("")) {
            throw new AccountNullException("账号信息不能为空！");
        }
        // 查询账号
        Account acc = dao.selectOne(id, password);
        if (acc == null) {
            throw new NotFoundException("账户密码不匹配！");
        }
        acc.withdraw(money);
        dao.update(acc);
        return dao.selectOne(id); // 确保返回的对象与文件中一致
    }

    /**
     * 5. 设置透支额度
     * 这个方法需要验证账户是否是信用账户 instanceof
     */
    @LogAction(value = "修改透支额度", whoArgIndex = 0)
    public Account updateCeiling(Long id, String password, double money)
            throws AccountNullException, NotFoundException, ATMException {
        if (id == null || password == null || password.equals("")) {
            throw new AccountNullException("账号信息不能为空！");
        }
        // 查询账号
        Account acc = dao.selectOne(id, password);
        if (acc == null) {
            throw new NotFoundException("账号密码不匹配！");
        }
        if (!(acc instanceof CreditAccount)) { // 验证账户是否是信用账户
            throw new TypeException("当前账户不是信用账户类型！不支持修改透支额度！");
        }
        ((CreditAccount) acc).setCeiling(money); // 设置透支额度
        dao.update(acc);
        return dao.selectOne(id);
    }

    /**
     * 6. 转账功能(transfer)
     */
    @LogAction(value = "转账", whoArgIndex = 0)
    public boolean transfer(Long from, String passwordFrom, Long to, double money)
            throws AccountNullException, BalanceNotEnoughException, NotFoundException, TransferException {
        // 1.转出/转入账户信息是否为空
        if (from == null || passwordFrom == null || passwordFrom.equals("")) {
            throw new AccountNullException("转出账户信息不能为空！");
        }
        if (to == null) {
            throw new AccountNullException("转入账户信息不能为空！");
        }
        // 2.转账金额验证
        if (money <= 0) {
            throw new BalanceNotEnoughException("转账金额必须大于0！");
        }
        // 3.账户不能是同一个
        if (from.equals(to)) {
            throw new TransferException("转入转出账户不能是同一个！");
        }
        // 4.账户验证
        Account fromAcc = dao.selectOne(from, passwordFrom); // 转出账户
        Account toAcc = dao.selectOne(to); // 转入账户
        if (fromAcc == null) {
            throw new NotFoundException("转出账户不存在或密码错误！");
        }
        if (toAcc == null) {
            throw new NotFoundException("转入账户不存在！");
        }

        // 5.检查转出账户余额是否充足
        if (fromAcc instanceof SavingAccount && fromAcc.getBalance() < money) {
            throw new BalanceNotEnoughException("储蓄账户余额不足！");
        } else if (fromAcc instanceof CreditAccount
                && fromAcc.getBalance() + ((CreditAccount) fromAcc).getCeiling() < money) {
            throw new BalanceNotEnoughException("信用账户余额和透支额度不足！");
        }

        // 6.转账逻辑
        fromAcc.withdraw(money); // 转出账户取钱
        toAcc.deposit(money); // 转入账户存钱
        dao.update(fromAcc);
        dao.update(toAcc);
        return true;
    }

    /**
     * 用户贷款
     * 
     * @param id    用户ID
     * @param money 贷款金额
     * @return Account 银行账户
     */
    @LogAction(value = "申请贷款", whoArgIndex = 0)
    public Account requestLoan(Long id, double money)
            throws AccountNullException, NotFoundException, TypeException, LoanException {
        if (id == null) {
            throw new AccountNullException("账户信息不能为空！");
        }
        // 账户验证
        Account acc = dao.selectOne(id);
        if (acc == null) {
            throw new NotFoundException("账户不存在！");
        }
        if (!(acc instanceof Loanable)) {
            throw new AccountNullException("当前账户不支持贷款！");
        }
        ((Loanable) acc).requestLoan(money); // 账户贷款
        dao.update(acc); // 修改后，进行更新
        return dao.selectOne(id); // 确保返回的对象与文件中一致
    }

    /**
     * 用户还款
     * 
     * @param id    用户ID
     * @param money 还款金额
     * @return Account 银行账户
     */
    @LogAction(value = "还贷", whoArgIndex = 0)
    public Account payLoan(Long id, double money) throws AccountNullException, NotFoundException, LoanException {
        if (id == null) {
            throw new AccountNullException("账户信息不能为空！");
        }
        // 账户验证
        Account acc = dao.selectOne(id);
        if (acc == null) {
            throw new NotFoundException("账户不存在！");
        }
        if (!(acc instanceof Loanable)) {
            System.out.println("当前账户不支持还贷！");
        }
        ((Loanable) acc).payLoan(money); // 账户还款
        dao.update(acc); // 修改后，进行更新
        return dao.selectOne(id); // 确保返回的对象与文件中一致
    }

    /**
     * 统计银行所有账户余额总数
     */
    public double balanceSum() {
        double sum = 0.0;
        for (Account acc : dao.selectAll()) {
            if (acc != null) { // 如果账户不为空
                sum += acc.getBalance();
            }
        }
        return sum;
    }

    /**
     * 统计所有信用账户透支额度总数
     */
    public double ceilingSum() {
        double sum = 0.0;
        for (Account acc : dao.selectAll()) {
            if (acc != null) { // 如果账户不为空
                if (acc instanceof CreditAccount) {
                    sum += ((CreditAccount) acc).getCeiling();
                }
            }
        }
        return sum;
    }

    /**
     * 统计所有账户贷款的总额
     * 
     * @return double
     */
    public double loanSum() {
        double sum = 0.0;
        for (Account acc : dao.selectAll()) {
            if (acc != null) { // 如果账户不为空
                // 如果是可以贷款的账户
                if (acc instanceof LoanableSavingsAccount) { // 如果是可贷款储蓄账户
                    sum += ((LoanableSavingsAccount) acc).getLoanAmount();
                }
                if (acc instanceof LoanableCreditAccount) { // 如果是可贷款信用账户
                    sum += ((LoanableCreditAccount) acc).getLoanAmount();
                }
            }
        }
        return sum;
    }

    /**
     * 打印所有用户的总资产排名
     */
    public void printUserAssetRanking() {
        // 一个身份证 ——> 多个账号
        Map<String, List<Account>> map = new HashMap<>();
        // 每个身份证下的总资产
        ArrayList<RankVo> ranks = new ArrayList<>();

        // 遍历每个账户
        for (Account acc : dao.selectAll()) {
            if (!map.containsKey(acc.getPersonId())) {
                // 如果没有身份证记录，创建一个
                map.put(acc.getPersonId(), new ArrayList<>());
            }
            // 把当前账户添加到这个身份证下
            map.get(acc.getPersonId()).add(acc);
        }

        // 计算每个身份证下的所有账户总资产
        Set<String> keys = map.keySet();
        for (String key : keys) {
            Double total = 0.0;
            // 计算当前身份证下的总资产
            for (Account acc : map.get(key)) {
                total += acc.getBalance();
            }
            // 获取对应身份证的姓名（取第一个账户的名字即可）
            String name = map.get(key).get(0).getName();
            // 创建RankVo对象
            ranks.add(new RankVo(key, name, total));
        }

        // 排序
        Collections.sort(ranks);

        // 打印结果
        for (RankVo r : ranks) {
            System.out.println(r);
        }
    }

    /**
     * 获取总计表数据
     */
    public Map<String, Double> getTotalStatistics() {
        Map<String, Double> map = new LinkedHashMap<>();
        map.put("账户余额总数", balanceSum());
        map.put("透支额度总数", ceilingSum());
        map.put("贷款额总数", loanSum());
        return map;
    }

    /**
     * 获取资产排名
     */
    public List<Map<String, Object>> getAssetRank() {
        // 身份证 -> 账户集合
        Map<String, List<Account>> map = new HashMap<>();
        for (Account acc : dao.selectAll()) {
            map.computeIfAbsent(acc.getPersonId(), k -> new ArrayList<>()).add(acc);
        }

        // 统计每个身份证的总资产
        List<Map<String, Object>> rankList = new ArrayList<>();
        for (String pid : map.keySet()) {
            double total = 0;
            for (Account acc : map.get(pid)) {
                total += acc.getBalance();
            }
            Map<String, Object> record = new HashMap<>();
            record.put("personId", pid);
            record.put("total", total);
            rankList.add(record);
        }

        // 按总资产降序排序
        rankList.sort((a, b) -> Double.compare((Double) b.get("total"), (Double) a.get("total")));

        // 添加排名
        for (int i = 0; i < rankList.size(); i++) {
            rankList.get(i).put("rank", i + 1);
        }
        return rankList;
    }

    /**
     * 查询某身份证下所有账户
     */
    public List<Account> getAccountsByPersonId(String personId) {
        List<Account> all = dao.selectAll();
        List<Account> result = new ArrayList<>();
        for (Account acc : all) {
            if (acc.getPersonId().equals(personId)) {
                result.add(acc);
            }
        }
        return result;
    }

    // 新增：按ID查询账户（用于客户端刷新）
    public Account getAccountById(Long id) throws AccountNullException, NotFoundException {
        if (id == null) {
            throw new AccountNullException("账户ID不能为空！");
        }
        System.out.println("查询账户ID: " + id);
        Account acc = dao.selectOne(id);
        if (acc == null) {
            System.out.println("账户ID " + id + " 在数据库中不存在！");
            throw new NotFoundException("账户不存在！");
        }
        System.out.println("查询到账户: " + acc.getId() + ", 姓名: " + acc.getName());
        return acc;
    }
}
